//=============================================================================
// PNT.fx by Bart Uyttenhove 2009
//
// 
//=============================================================================
#include "lighthelper.fx"
static const float gTexScale = 30;
static const float SHADOW_EPSILON = 0.001f;
static const float SMAP_SIZE = 1024.0f;
static const float SMAP_DX = 1.0f / SMAP_SIZE;
//Dit zijn percentage
//Vanaf
static const float gLow = 0.2f;
static const float gBase = 0.3f;
static const float gMid = 0.7f;
static const float gHigh = 1.0f;

#define FOGMODE_NONE    0
#define FOGMODE_LINEAR  1
#define FOGMODE_EXP     2
#define FOGMODE_EXP2    3
#define E 2.71828

RasterizerState BackCull
{
	CullMode = BACK;
};
RasterizerState FrontCull
{
	CullMode = FRONT;
};
RasterizerState NoneCull
{
	CullMode = NONE;
};
SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;// of Mirror of Clamp of Border
    AddressV = Wrap;// of Mirror of Clamp of Border
};
SamplerState samPoint
{
    Filter = MIN_MAG_MIP_POINT;
    AddressU = Clamp;// of Mirror of Clamp of Border
    AddressV = Clamp;// of Mirror of Clamp of Border
};
SamplerState samAnisotropic
{
	Filter = ANISOTROPIC ;
    AddressU = Border;// of Mirror of Clamp of Border
    AddressV = Border;// of Mirror of Clamp of Border
    BorderColor = float4(0.0f,0.0f,1.0f,1.0f);
};
SamplerState gTriLinearSam
{
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

//Texture2D gDiffuseMap;
Texture2D Texture1;
Texture2D Texture2;
Texture2D Texture3;
Texture2D Texture4;
Texture2D Blendmap;

Texture2D gShadowMap;

cbuffer cbPerObject
{
	float4x4 gLightWVP; 
	float4x4 gWVP;
	float4x4 gWorld;
	float3 EyePos;
	Light2 gLight;
	float HeightOffset;
	float WaterHeight;
	float4 ClipRefraction;
	bool Clipping;

	int      g_fogMode = FOGMODE_LINEAR;
	float    g_fogStart = 50.0f;
    float    g_fogEnd = 300.0f;
    float    g_fogDensity = 0.05f;
	float    g_fogMaxHeight = 0.05f;
    float4   g_fogColor = float4(0.7f,1.0f,1.0f,1);
};

// we gebruiken deze structs omdat ze oa de leesbaarheid verhogen
// POSITION, COLOR en NORMAL zijn semantics en komen overeen met

// de InputLayout
struct VS_IN
{
	float3 Pos : POSITION;
	float3 Norm : NORMAL;
    float2 Tex : TEXCOORD;
};
struct VS_OUT
{
	float4 Pos : SV_POSITION;
	float4 PosW : TEXCOORD0;
	float3 Norm : NORMAL;
	float2 tiledUV : TEXCOORD2;
    float2 stretchedUV : TEXCOORD1;
	float4 ShadowTexjC : TEXCOORD3;
	float fogFactor : FOGDISTANCE;
};
struct VS_OUT_REFLECTION
{
	float4 Pos : SV_POSITION;
	float4 PosW : TEXCOORD0;
	float3 Norm : NORMAL;
	float2 tiledUV : TEXCOORD2;
    float2 stretchedUV : TEXCOORD1;
	float4 ShadowTexjC : TEXCOORD3;
	float clip : SV_ClipDistance0;
};
struct VS_OUT_SHADOW
{
	float4 Pos : SV_POSITION;
	float4 PosW : TEXCOORD0;
};
VS_OUT VS(	VS_IN vIn)
{
	VS_OUT vOut = (VS_OUT)0;//fill with 0
	vOut.PosW = mul(float4(vIn.Pos,1.0f),gWorld);
	// multiply position of vertex and wvp matrix
	vOut.Pos = mul(float4(vIn.Pos, 1.0f), gWVP);
	// rotate normal with world matrix
	vOut.Norm = mul(vIn.Norm, gWorld);

	vOut.tiledUV     = gTexScale*vIn.Tex;
	vOut.stretchedUV = vIn.Tex;

	vOut.ShadowTexjC = mul(float4(vIn.Pos + vIn.Norm, 1.0f), gLightWVP);

	return vOut;
}
VS_OUT_REFLECTION VSREFLECTION(	VS_IN vIn)
{
	VS_OUT_REFLECTION vOut = (VS_OUT_REFLECTION)0;//fill with 0
	vOut.PosW = mul(float4(vIn.Pos,1.0f),gWorld);
	// multiply position of vertex and wvp matrix
	vOut.Pos = mul(float4(vIn.Pos, 1.0f), gWVP);
	// rotate normal with world matrix
	vOut.Norm = mul(vIn.Norm, (float3x3)gWorld);

	vOut.tiledUV     = gTexScale*vIn.Tex;
	vOut.stretchedUV = vIn.Tex;

	vOut.ShadowTexjC = mul(float4(vIn.Pos + vIn.Norm, 1.0f), gLightWVP);
	vOut.clip = dot(vOut.PosW, ClipRefraction);
	return vOut;
}
VS_OUT VSSHADOW(	VS_IN vIn)
{
	VS_OUT vOut = (VS_OUT)0;//fill with 0
	vOut.PosW = mul(float4(vIn.Pos,1.0f),gWorld);
	// multiply position of vertex and wvp matrix
	vOut.Pos = mul(float4(vIn.Pos, 1.0f), gWVP);
	// rotate normal with world matrix
	vOut.Norm = mul(vIn.Norm, (float3x3)gWorld);

	vOut.tiledUV     = gTexScale*vIn.Tex;
	vOut.stretchedUV = vIn.Tex;

	vOut.ShadowTexjC = mul(float4(vIn.Pos, 1.0f), gLightWVP);

	return vOut;
}
float4 PSSHADOW(	VS_OUT pIn) : SV_Target
{
	return float4(1, 1, 0, 1.0f);
}
float CalcFogFactor( float d )
{
    float fogCoeff = 1.0;
    
    if( FOGMODE_LINEAR == g_fogMode )
    {
        fogCoeff = (g_fogEnd - d)/(g_fogEnd - g_fogStart);
    }
    else if( FOGMODE_EXP == g_fogMode )
    {
        fogCoeff = 1.0 / pow( E, d*g_fogDensity );
    }
    else if( FOGMODE_EXP2 == g_fogMode )
    {
        fogCoeff = 1.0 / pow( E, d*d*g_fogDensity*g_fogDensity );
    }
    
    return clamp( fogCoeff, 0, 1 );
}
float CalcShadowFactor(float4 projTexC)
{
	// Complete projection by doing division by w.
	projTexC.xyz /= projTexC.w;
	
	// Points outside the light volume are in shadow.
	if( projTexC.x < -1.0f || projTexC.x > 1.0f || 
	    projTexC.y < -1.0f || projTexC.y > 1.0f ||
	    projTexC.z < 0.0f )
	    return 0.0f;
	    
	// Transform from NDC space to texture space.
	projTexC.x = +0.5f*projTexC.x + 0.5f;
	projTexC.y = -0.5f*projTexC.y + 0.5f;
	
	// Depth in NDC space.
	float depth = projTexC.z;

	// Sample shadow map to get nearest depth to light.
	float s0 = gShadowMap.Sample(samPoint, projTexC.xy).r;
	float s1 = gShadowMap.Sample(samPoint, projTexC.xy + float2(SMAP_DX, 0)).r;
	float s2 = gShadowMap.Sample(samPoint, projTexC.xy + float2(0, SMAP_DX)).r;
	float s3 = gShadowMap.Sample(samPoint, projTexC.xy + float2(SMAP_DX, SMAP_DX)).r;
	
	// Is the pixel depth <= shadow map value?
	float result0 = depth <= s0 + SHADOW_EPSILON;
	float result1 = depth <= s1 + SHADOW_EPSILON;
	float result2 = depth <= s2 + SHADOW_EPSILON;
	float result3 = depth <= s3 + SHADOW_EPSILON;	
	
	// Transform to texel space.
	float2 texelPos = SMAP_SIZE*projTexC.xy;
	
	// Determine the interpolation amounts.
	float2 t = frac( texelPos );
	
	// Interpolate results.
	return lerp( lerp(result0, result1, t.x), 
	             lerp(result2, result3, t.x), t.y);
}
float4 PSAnisotropic(	VS_OUT pIn) : SV_Target
{
	float3 c0 = Texture1.Sample( gTriLinearSam, pIn.tiledUV ).rgb;
	float3 c1 = Texture2.Sample( gTriLinearSam, pIn.tiledUV ).rgb;
	float3 c2 = Texture3.Sample( gTriLinearSam, pIn.tiledUV ).rgb;
	float3 c3 = Texture4.Sample( gTriLinearSam, pIn.tiledUV ).rgb;

	float4 t = Blendmap.Sample( gTriLinearSam, pIn.stretchedUV ); 
    
    float3 C = c0;
    C = lerp(C, c0, t.r);
    C = lerp(C, c1, t.g);
    C = lerp(C, c2, t.b);
    C = lerp(C, c3, t.a);

	float shadowFactor = CalcShadowFactor(pIn.ShadowTexjC);

	float4 Diff = float4(C, 1.0f);
	//return C;

	float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
	// Map [0,1] --> [0,256]
	spec.a *= 256.0f;

	//soms wordt door interpolatie de lengte van de vector aangepast.
   	pIn.Norm=-normalize(pIn.Norm);

	// Compute the lit color for this pixel.
    SurfaceInfo v = {pIn.PosW.xyz, pIn.Norm, Diff, spec};
	float3 litColor = ParallelLight(v, gLight, EyePos, shadowFactor);

	//return Diff * shadowFactor;
	float fog = CalcFogFactor(length(pIn.PosW - EyePos));
	float4 finalcolor = fog * float4(litColor, 1.0f) + (1.0 - fog)*g_fogColor;

	return finalcolor;
}
float4 PSReflection(	VS_OUT_REFLECTION pIn) : SV_Target
{
	float3 c0 = Texture1.Sample( gTriLinearSam, pIn.tiledUV ).rgb;
	float3 c1 = Texture2.Sample( gTriLinearSam, pIn.tiledUV ).rgb;
	float3 c2 = Texture3.Sample( gTriLinearSam, pIn.tiledUV ).rgb;
	float3 c3 = Texture4.Sample( gTriLinearSam, pIn.tiledUV ).rgb;

	float4 t = Blendmap.Sample( gTriLinearSam, pIn.stretchedUV ); 
    
    float3 C = c0;
    C = lerp(C, c0, t.r);
    C = lerp(C, c1, t.g);
    C = lerp(C, c2, t.b);
    C = lerp(C, c3, t.a);

	float4 Diff = float4(C, 1.0f);
	//return C;

	float shadowFactor = CalcShadowFactor(pIn.ShadowTexjC);

	float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
	// Map [0,1] --> [0,256]
	spec.a *= 256.0f;

	//soms wordt door interpolatie de lengte van de vector aangepast.
   	pIn.Norm=-normalize(pIn.Norm);

	// Compute the lit color for this pixel.
    SurfaceInfo v = {pIn.PosW.xyz, pIn.Norm, Diff, spec};
	float3 litColor = ParallelLight(v, gLight, EyePos, shadowFactor);

	//return Diff * shadowFactor;

	return float4(litColor, 1.0f);
}
technique10 TechAnisotropic
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PSAnisotropic() ) );
		SetRasterizerState(BackCull);
  }
}
technique10 Reflection
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VSREFLECTION() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PSReflection() ) );
		SetRasterizerState(FrontCull);
  }
}
technique10 Shadow
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VSSHADOW() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PSSHADOW() ) );
		SetRasterizerState(NoneCull);
  }
}